package com.neborosoft.lib.collections.list;

import com.neborosoft.lib.collections.Container;
import com.neborosoft.lib.collections.IterableConcat;
import org.apache.commons.collections15.IteratorUtils;

import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: stikhonenko
 * Date: 12/6/12
 * Time: 6:18 PM
 * To change this template use File | Settings | File Templates.
 */
public abstract class BasicListConcatenation<T> extends Container<T> implements List<T> {
    protected abstract List<T> getList(int index);
    protected abstract int getListsCount();

    protected int getListSize(int index){
        List list = getList(index);
        if(list != null){
            return list.size();
        }
        else {
            return 0;
        }
    }

    protected List< List<T> > createLists(){
        return new ArrayList< List<T> >();
    }

    protected static class Index{
        int listIndex;
        int indexInList;
    }
    protected Index calculateIndex(int requestIndex){
        int count = getListsCount();
        for(int listIndex = 0; listIndex < count; listIndex++){
            int listSize = getListSize(listIndex);

            if(requestIndex < listSize){
                Index result = new Index();
                result.listIndex = listIndex;
                result.indexInList = requestIndex;
                return result;
            }

            requestIndex -= listSize;
        }

        throw new IndexOutOfBoundsException();
    }

    @Override
    public void add(int i, T object) {
        if(i == size()){
            getList(getListsCount() - 1).add(object);
        }
        Index index = calculateIndex(i);

        List list = getList(index.listIndex);
        list.add(index.indexInList, object);
    }

    @Override
    public boolean add(T object) {
        return getList(getListsCount() - 1).add(object);
    }

    @Override
    public boolean addAll(int i, Collection<? extends T> collection) {
        if(i == size()){
            return getList(getListsCount() - 1).addAll(collection);
        }
        Index index = calculateIndex(i);

        List list = getList(index.listIndex);
        return list.addAll(index.indexInList, collection);
    }

    @Override
    public boolean addAll(Collection<? extends T> collection) {
        return getList(getListsCount() - 1).addAll(collection);
    }

    @Override
    public void clear() {
        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            list.clear();
        }
    }

    @Override
    public boolean contains(Object o) {
        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            if(list.contains(o)){
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean containsAll(Collection<?> objects) {
        for(Object i : objects){
            if(!contains(i)){
                return false;
            }
        }

        return true;
    }

    @Override
    public T get(int i) {
        Index index = calculateIndex(i);
        List<T> list = getList(index.listIndex);

        return list.get(index.indexInList);
    }

    @Override
    public int indexOf(Object object) {
        int listIndex = 0;
        int resultIndex = 0;

        for(; listIndex < getListsCount(); listIndex++){
            List<T> list = getList(listIndex);
            if(list == null){
                continue;
            }

            int indexOf = list.indexOf(object);
            if(indexOf >= 0){
                return resultIndex + indexOf;
            }

            resultIndex += list.size();
        }

        return -1;
    }

    @Override
    public boolean isEmpty() {
        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            if(!list.isEmpty()){
                return false;
            }
        }

        return true;
    }

    @Override
    public Iterator<T> iterator() {
        Iterable<T>[] iterators = new Iterable[getListsCount()];
        for(int i = 0; i < getListsCount(); i++){
            iterators[i] = getList(i);
        }

        IterableConcat<T> concat = new IterableConcat<T>(iterators);
        return concat.iterator();
    }

    @Override
    public int lastIndexOf(Object object) {
        int listIndex = 0;
        int resultIndex = 0;
        int lastFoundIndex = -1;

        for(; listIndex < getListsCount(); listIndex++){
            List<T> list = getList(listIndex);
            if(list == null){
                continue;
            }

            int indexOf = list.indexOf(object);
            if(indexOf >= 0){
                lastFoundIndex = resultIndex + indexOf;
            }

            resultIndex += list.size();
        }

        return lastFoundIndex;
    }

    @Override
    public ListIterator<T> listIterator() {
        Iterator<T> iterator = iterator();
        return IteratorUtils.toListIterator(iterator);
    }

    @Override
    public ListIterator<T> listIterator(int i) {
        throw new UnsupportedOperationException();
    }

    @Override
    public T remove(int i) {
        Index index = calculateIndex(i);
        List<T> list = getList(index.indexInList);
        return list.remove(index.indexInList);
    }

    @Override
    public boolean remove(Object object) {
        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            if(list.remove(object)){
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean removeAll(Collection<?> objects) {
        boolean listModified = false;

        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            if(list.removeAll(objects)){
                listModified = true;
            }
        }

        return listModified;
    }

    @Override
    public boolean retainAll(Collection<?> objects) {
        boolean listModified = false;

        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            if(list.retainAll(objects)){
                listModified = true;
            }
        }

        return listModified;
    }

    @Override
    public T set(int i, T value) {
        Index index = calculateIndex(i);
        List<T> list = getList(index.listIndex);

        return list.set(index.indexInList, value);
    }

    @Override
    public int size() {
        int size = 0;
        for(int i = 0; i < getListsCount(); i++){
            List list = getList(i);
            size += list.size();
        }

        return size;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException();
    }

    @Override
    public List<T> subList(int a, int b) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object[] toArray() {
        throw new UnsupportedOperationException();
    }

    @Override
    public <T1 extends Object> T1[] toArray(T1[] t1s) {
        throw new UnsupportedOperationException();
    }
}
